package nl.hanze.t23i.gamemodule.game;
 
import java.awt.Component;
import java.io.PrintStream;
import java.util.*;
import nl.hanze.t23i.gamemodule.extern.AbstractGameModule;
 
// Referenced classes of package nl.hanze.t23i.gamemodule.game:
//            GameView, Position
 
public class TicTacToe extends AbstractGameModule
{ 
    public TicTacToe(String p1, String p2)
    {
        super(p1, p2);
        rows = 3;
        columns = 3;
        reader = new Scanner(System.in);
        calculating = false;
        tempBoard = new HashMap();
        board = new int[rows][columns];
        random = new Random();
        side = random.nextInt(2);
        position = 2;
        clearBoard();
        initSide();
        moveDetails = null;
        playerResults = new HashMap();
        gameView = new GameView(playerOne, playerTwo);
    }
 
    public Component getView()
    {
        return gameView;
    }
 
    public String getTurnMessage()
        throws IllegalStateException
    {
        super.getTurnMessage();
        if(moveDetails != null)
            return moveDetails;
        else
            return "***NEW GAME***";
    }
 
    public void start()
        throws IllegalStateException
    {
        super.start();
        nextPlayer = playerOne;
    }
 
    public String getPlayerToMove()
        throws IllegalStateException
    {
        super.getPlayerToMove();
        return nextPlayer;
    }
 
    public void doPlayerMove(String player, String move)
        throws IllegalStateException
    {
        super.doPlayerMove(player, move);
        if(!nextPlayer.equals(player))
            throw new IllegalStateException("It is not player's turn");
        gameView.addText(String.format("%s: %s", new Object[] {
            player, move
        }));
        int moev = -1;
        try
        {
            moev = Integer.parseInt(move);
        }
        catch(NumberFormatException e)
        {
            illegalPlayerMove(player);
            return;
        }
        if(!moveOk(moev))
        {
            illegalPlayerMove(player);
            return;
        }
        place((int)Math.floor(moev / columns), moev % columns, player != playerOne ? 1 : 0);
        gameView.addText(toString());
        if(gameOver())
        {
            matchStatus = 1;
            moveDetails = "Afgelopen";
            if(positionValue() == 1)
            {
                playerResults.put(player, Integer.valueOf(0));
                playerResults.put(otherPlayer(player), Integer.valueOf(0));
            } else
            {
                playerResults.put(player, Integer.valueOf(1));
                playerResults.put(otherPlayer(player), Integer.valueOf(-1));
            }
        } else
        {
            moveDetails = move;
            nextPlayer();
        }
    }
 
    public String getMoveDetails()
        throws IllegalStateException
    {
        super.getMoveDetails();
        if(moveDetails == null)
            throw new IllegalStateException("No move has been done during this match");
        else
            return moveDetails;
    }
 
    public int getPlayerResult(String player)
        throws IllegalStateException
    {
        super.getPlayerResult(player);
        return ((Integer)playerResults.get(player)).intValue();
    }
 
    public int getPlayerScore(String player)
        throws IllegalStateException
    {
        super.getPlayerResult(player);
        return ((Integer)playerResults.get(player)).intValue();
    }
 
    public String getMatchResultComment()
        throws IllegalStateException
    {
        super.getMatchResultComment();
        return "Afgelopen";
    }
 
    public int getMatchStatus()
    {
        return matchStatus;
    }
 
    private void illegalPlayerMove(String player)
    {
        matchStatus = 1;
        moveDetails = "Illegal move";
        playerResults.put(otherPlayer(player), Integer.valueOf(1));
        playerResults.put(player, Integer.valueOf(-1));
    }
 
    private void nextPlayer()
    {
        side = side != 0 ? 0 : 1;
        nextPlayer = otherPlayer(nextPlayer);
    }
 
    private String otherPlayer(String player)
    {
        side = side != 1 ? 1 : 0;
        return player.equals(playerOne) ? playerTwo : playerOne;
    }
 
    private void initSide()
    {
        if(side == 1)
        {
            computerChar = 'X';
            humanChar = 'O';
        } else
        {
            computerChar = 'O';
            humanChar = 'X';
        }
    }
 
    public void setComputerPlays()
    {
        side = 1;
        initSide();
    }
 
    public void setHumanPlays()
    {
        side = 0;
        initSide();
    }
 
    public boolean computerPlays()
    {
        return side == 1;
    }
 
    public int chooseMove()
    {
        Best best = chooseMove(1, 0, 3, 0);
        return best.row * 3 + best.column;
    }
 
    private Best chooseMove(int side, int bot, int top, int depth)
    {
        position thisPosition = new position(board);
        int tableDepth = 5;
        int bestRow = 0;
        int bestColumn = 0;
        int value = 0;
        int simpleEval;
        if((simpleEval = positionValue()) != 2)
            return new Best(simpleEval);
        int opp;
        if(side == 1)
        {
            opp = 0;
            value = bot;
        } else
        {
            opp = 1;
            value = top;
        }
        if(depth == 0)
            tempBoard.clear();
        else
        if(depth >= 3 && depth <= tableDepth)
        {
            Integer prev = (Integer)tempBoard.get(thisPosition);
            if(prev != null)
                return new Best(prev.intValue());
        }
        for(int row = 0; row < 3 && bot < top; row++)
        {
            for(int column = 0; column < 3 && bot < top; column++)
                if(squareIsEmpty(row, column))
                {
                    place(row, column, side);
                    Best reply = chooseMove(opp, bot, top, depth + 1);
                    place(row, column, 2);
                    if(side == 1 && reply.val > value || side == 0 && reply.val < value)
                    {
                        value = reply.val;
                        if(side == 1)
                            bot = value;
                        else
                            top = value;
                        bestRow = row;
                        bestColumn = column;
                    }
                }
 
        }
 
        if(depth <= tableDepth)
            tempBoard.put(thisPosition, Integer.valueOf(value));
        return new Best(value, bestRow, bestColumn);
    }
 
    public boolean moveOk(int move)
    {
        return move >= 0 && move < rows * columns && board[move / columns][move % columns] == 2;
    }
 
    public void playMove(int move)
    {
        board[move / columns][move % columns] = side;
        if(side == 1)
            side = 0;
        else
            side = 1;
    }
 
    private void clearBoard()
    {
        for(int i = 0; i < rows; i++)
        {
            for(int j = 0; j < columns; j++)
                board[i][j] = 2;
 
        }
 
    }
 
    public boolean isAWin(int side)
    {
        return side == 0 && positionValue() == 0 || side == 1 && positionValue() == 3;
    }
 
    private void place(int row, int column, int piece)
    {
        board[row][column] = piece;
    }
 
    private boolean squareIsEmpty(int row, int column)
    {
        return board[row][column] == 2;
    }
 
    public int positionValue()
    {
        boolean full = true;
        for(int i = 0; i < rows; i++)
        {
            for(int j = 0; j < columns; j++)
            {
                if(board[i][j] == 2)
                    full = false;
                if(board[i][j] == 0)
                {
                    if(i + 2 < rows && board[i + 1][j] == 0 && board[i + 2][j] == 0)
                        return 0;
                    if(j + 2 < columns && board[i][j + 1] == 0 && board[i][j + 2] == 0)
                        return 0;
                    if(i + 2 < rows && j + 2 < columns && board[i + 1][j + 1] == 0 && board[i + 2][j + 2] == 0)
                        return 0;
                    if(i + 2 < rows && j - 2 >= 0 && board[i + 1][j - 1] == 0 && board[i + 2][j - 2] == 0)
                        return 0;
                } else
                if(board[i][j] == 1)
                {
                    if(i + 2 < rows && board[i + 1][j] == 1 && board[i + 2][j] == 1)
                        return 3;
                    if(j + 2 < columns && board[i][j + 1] == 1 && board[i][j + 2] == 1)
                        return 3;
                    if(i + 2 < rows && j + 2 < columns && board[i + 1][j + 1] == 1 && board[i + 2][j + 2] == 1)
                        return 3;
                    if(i + 2 < rows && j - 2 >= 0 && board[i + 1][j - 1] == 1 && board[i + 2][j - 2] == 1)
                        return 3;
                }
            }
 
        }
 
        return !full ? 2 : 1;
    }
 
    public static void main(String args[])
    {
        TicTacToe t;
        do
        {
            for(t = new TicTacToe("a", "b"); !t.gameOver(); System.out.println(t))
                t.doPlayerMove(t.nextPlayer, (new StringBuilder()).append(t.move()).toString());
 
            System.out.println((new StringBuilder("Game over ")).append(t.winner()).append(" wins").toString());
        } while(t.nextGame());
    }
 
    private int move()
    {
        int humanMove;
        do
        {
            System.out.print("Human move    = ");
            humanMove = reader.nextInt();
        } while(!moveOk(humanMove));
        return humanMove;
    }
 
    private boolean nextGame()
    {
        Character yn;
        do
        {
            System.out.print("next Game? enter Y/N: ");
            yn = Character.valueOf(reader.next().charAt(0));
            System.out.println((new StringBuilder()).append(yn).toString());
        } while(yn.charValue() != 'Y' && yn.charValue() != 'y' && yn.charValue() != 'N' && yn.charValue() != 'n');
        return yn.charValue() == 'Y' || yn.charValue() == 'y';
    }
 
    public String toString()
    {
        String ret = "";
        for(int row = 0; row < board.length; row++)
        {
            for(int column = 0; column < board[row].length; column++)
                if(board[row][column] == 0)
                    ret = (new StringBuilder(String.valueOf(ret))).append(humanChar).toString();
                else
                if(board[row][column] == 1)
                    ret = (new StringBuilder(String.valueOf(ret))).append(computerChar).toString();
                else
                    ret = (new StringBuilder(String.valueOf(ret))).append(".").toString();
 
        }
 
        return ret;
    }
 
    public boolean gameOver()
    {
        if(!calculating)
        {
            position = positionValue();
            return position != 2;
        } else
        {
            return false;
        }
    }
 
    public String winner()
    {
        if(position == 3)
            return "computer";
        if(position == 0)
            return "human";
        else
            return "nobody";
    }
 
    public static final String GAME_TYPE = "TicTacToe";
    private int rows;
    private int columns;
    private static final int HUMAN = 0;
    private static final int COMPUTER = 1;
    public static final int EMPTY = 2;
    public static final int HUMAN_WIN = 0;
    public static final int DRAW = 1;
    public static final int UNCLEAR = 2;
    public static final int COMPUTER_WIN = 3;
    private Scanner reader;
    private boolean calculating;
    private HashMap tempBoard;
    private int board[][];
    private Random random;
    private int side;
    private int position;
    private char computerChar;
    private char humanChar;
    private GameView gameView;
    private String nextPlayer;
    private String moveDetails;
    private HashMap playerResults;
    
    private class Best
    {
     
        int row;
        int column;
        int val;
        final TicTacToe this$0;
     
        public Best(int v)
        {
            this(v, 0, 0);
        }
     
        public Best(int v, int r, int c)
        {
        	super();
            this$0 = TicTacToe.this;
            val = v;
            row = r;
            column = c;
        }
    }
}